﻿using System;
using System.Web;
using JetBrains.Annotations;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Web.Interface;
using MugenInjection.Web.Settings;

namespace MugenInjection.Web
{
    /// <summary>
    /// Base implementation of <see cref="HttpApplication"/> that adds injection support.
    /// </summary>
    public abstract class MugenInjectionHttpApplication : HttpApplication
    {
        #region Fields

        private static readonly object Locker = new object();
        private static ApplicationConfiguration _applicationConfiguration;

        #endregion

        #region Events

        internal static event Action<IApplicationConfiguration> ApplicationConfigurationInit;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the configuration for current application.
        /// </summary>
        public static IApplicationConfiguration ApplicationConfiguration
        {
            get
            {
                Validate.EnsureIsInitializedValue(_applicationConfiguration, "ApplicationConfiguration");
                return _applicationConfiguration;
            }
#if DEBUG
            set { _applicationConfiguration = (ApplicationConfiguration)value; }
#else
            internal set
            {
                Validate.PropertyNotNull(value, "ApplicationConfiguration");
                _applicationConfiguration = (ApplicationConfiguration)value;
            }
#endif
        }

        /// <summary>
        /// Gets the status of the current <see cref="MugenInjectionHttpApplication"/>.
        /// </summary>
        public static bool IsInitialized{ get; private set; }

        #endregion

        #region Methods

        /// <summary>
        /// Starts the application.
        /// </summary>
        public void Application_Start()
        {
            lock (Locker)
            {
                _applicationConfiguration = new ApplicationConfiguration(CreateInjector());
                Configure(_applicationConfiguration);
                IsInitialized = true;
                OnApplicationConfigurationInit(_applicationConfiguration);
                _applicationConfiguration.OnApplicationStarting(this);
                _applicationConfiguration.OnApplicationStarted(this);
                OnApplicationStarted();
            }
        }

        /// <summary>
        /// Releases the configuration on application end.
        /// </summary>
        public void Application_End()
        {
            lock (Locker)
            {
                if (_applicationConfiguration != null)
                    _applicationConfiguration.OnApplicationStopped(this);
                OnApplicationStopped();
                if (_applicationConfiguration != null)
                    _applicationConfiguration.Dispose();
            }
        }

        /// <summary>
        /// Handles the Start event of the Session.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Session_Start(object sender, EventArgs e)
        {
            _applicationConfiguration.OnSessionStart(this);
            OnSessionStart(sender, e);
        }

        /// <summary>
        /// Handles the End event of the Session.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Session_End(object sender, EventArgs e)
        {
            _applicationConfiguration.OnSessionEnd(this);
            OnSessionEnd(sender, e);
        }

        /// <summary>
        /// Handles the BeginRequest event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            _applicationConfiguration.OnBeginRequest(this);
            OnApplicationBeginRequest(sender, e);
        }

        /// <summary>
        /// Handles the EndRequest event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Application_EndRequest(object sender, EventArgs e)
        {
            _applicationConfiguration.OnEndRequest(this);
            OnApplicationEndRequest(sender, e);
        }


        /// <summary>
        /// Handles the AuthenticateRequest event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {
            _applicationConfiguration.OnAuthenticateRequest(this);
            OnApplicationAuthenticateRequest(sender, e);
        }

        /// <summary>
        /// Handles the Error event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Application_Error(object sender, EventArgs e)
        {
            _applicationConfiguration.OnApplicationError(this);
            OnApplicationError(sender, e);
        }

        private static void OnApplicationConfigurationInit(IApplicationConfiguration obj)
        {
            Action<IApplicationConfiguration> handler = ApplicationConfigurationInit;
            if (handler != null) handler(obj);
        }

        #endregion

        #region Abstract or virtual

        /// <summary>
        /// Creates the injector that will manage your application.
        /// </summary>
        /// <returns>The created injector.</returns>
        protected abstract IInjector CreateInjector();

        /// <summary>
        /// Configure current <see cref="MugenInjectionHttpApplication"/>
        /// </summary>
        /// <param name="applicationConfiguration">Configuration instance.</param>
        protected abstract void Configure(IApplicationConfiguration applicationConfiguration);

        /// <summary>
        /// Called when the session is started.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnSessionStart(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Called when the session is ended.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnSessionEnd(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Called when the request is started.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnApplicationBeginRequest(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Called when the request is ended.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnApplicationEndRequest(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Handles the AuthenticateRequest event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnApplicationAuthenticateRequest(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Handles the Error event of the Application.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnApplicationError(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Called when the application is started.
        /// </summary>
        protected virtual void OnApplicationStarted()
        {
        }

        /// <summary>
        /// Called when the application is stopped.
        /// </summary>
        protected virtual void OnApplicationStopped()
        {
        }

        #endregion
    }
}